package com.binance.notification.web.push;

import java.util.List;

import org.apache.commons.lang.StringUtils;

import com.google.common.collect.Lists;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;

import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.connection.HttpProxy;
import cn.jiguang.common.connection.NativeHttpClient;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.BaseResult;
import cn.jiguang.common.resp.ResponseWrapper;
import cn.jiguang.common.utils.Preconditions;
import cn.jpush.api.push.CIDResult;
import cn.jpush.api.push.model.PushPayload;

public class PushClient {
    private final ApacheHttpClient _httpClient;
    private final NativeHttpClient _nativeHttpClient;
    private String _baseUrl;
    private String _pushPath;
    private String _pushValidatePath;
    private JsonParser _jsonParser = new JsonParser();

    // If not present, true by default.
    private int _apnsProduction;

    // If not present, the default value is 86400(s) (one day)
    private long _timeToLive;


    public PushClient(String masterSecret, String appKey, HttpProxy proxy, ClientConfig conf) {
        ServiceHelper.checkBasic(appKey, masterSecret);

        this._baseUrl = (String) conf.get(ClientConfig.PUSH_HOST_NAME);
        this._pushPath = (String) conf.get(ClientConfig.PUSH_PATH);
        this._pushValidatePath = (String) conf.get(ClientConfig.PUSH_VALIDATE_PATH);

        this._apnsProduction = (Integer) conf.get(ClientConfig.APNS_PRODUCTION);
        this._timeToLive = (Long) conf.get(ClientConfig.TIME_TO_LIVE);

        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        this._httpClient = new ApacheHttpClient(authCode, proxy, conf);
        this._nativeHttpClient = new NativeHttpClient(authCode, proxy, conf);
    }

    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common
     * cn.jiguang.common.ClientConfig#setGlobalPushSetting} instead of this method.
     *
     * @param apnsProduction Global APNs environment setting. It will override PushPayload Options.
     * @param timeToLive Global time_to_live setting. It will override PushPayload Options.
     */
    @Deprecated
    public void setDefaults(boolean apnsProduction, long timeToLive) {
        if (apnsProduction) {
            _apnsProduction = 1;
        } else {
            _apnsProduction = 0;
        }
        this._timeToLive = timeToLive;
    }

    public void setBaseUrl(String baseUrl) {
        this._baseUrl = baseUrl;
    }

    private String pushPayloadsToString(List<PushPayload> pushPayload) {
        if (pushPayload == null || pushPayload.isEmpty()) {
            throw new IllegalArgumentException("pushPayload should not be null");
        }
        if (pushPayload.size() == 1) {
            if (pushPayload.get(0) == null) {
                throw new IllegalArgumentException("pushPayload should not be null");
            }
            return pushPayload.get(0).toString();
        }
        List<String> payloadList = Lists.newArrayList();
        for (PushPayload payload : pushPayload) {
            if (payload == null) {
                throw new IllegalArgumentException("pushPayload should not be null");
            }
            if (_apnsProduction > 0) {
                payload.resetOptionsApnsProduction(true);
            } else if (_apnsProduction == 0) {
                payload.resetOptionsApnsProduction(false);
            }
            if (_timeToLive >= 0) {
                payload.resetOptionsTimeToLive(_timeToLive);
            }
            payloadList.add(payload.toString());
        }
        return "[" + StringUtils.join(payloadList, ",") + "]";
    }

    public PushResult sendPushPayload(List<PushPayload> pushPayload) {
        ResponseWrapper response = _httpClient.sendPost(_baseUrl + _pushPath, pushPayloadsToString(pushPayload));
        return PushResult.fromResponse(response);
    }

    public PushResult sendPushPayloadValidate(List<PushPayload> pushPayload) {
        ResponseWrapper response =
                _httpClient.sendPost(_baseUrl + _pushValidatePath, pushPayloadsToString(pushPayload));

        return PushResult.fromResponse(response);
    }

    private String payloadStringsToString(List<String> payloadString) {
        if (payloadString == null || payloadString.isEmpty()) {
            throw new IllegalArgumentException("pushPayload should not be null");
        }
        if (payloadString.size() == 1) {
            try {
                _jsonParser.parse(payloadString.get(0));
            } catch (JsonParseException e) {
                throw new IllegalArgumentException("payloadString should be a valid JSON string.");
            }
            return payloadString.get(0);
        }
        List<String> payloadList = Lists.newArrayList();
        for (String payload : payloadString) {
            try {
                _jsonParser.parse(payload);
            } catch (JsonParseException e) {
                throw new IllegalArgumentException("payloadString should be a valid JSON string.");
            }
            payloadList.add(payload);
        }
        return "[" + StringUtils.join(payloadList, ",") + "]";
    }

    public PushResult sendPushPayloadString(List<String> payloadString) {
        ResponseWrapper response = _httpClient.sendPost(_baseUrl + _pushPath, payloadStringsToString(payloadString));
        return PushResult.fromResponse(response);
    }

    public PushResult sendPushPayloadStringValidate(List<String> payloadString) {
        ResponseWrapper response =
                _httpClient.sendPost(_baseUrl + _pushValidatePath, payloadStringsToString(payloadString));
        return PushResult.fromResponse(response);
    }

    /**
     * Get cid list, the data form of cid is appKey-uuid.
     *
     * @param count the count of cid list, from 1 to 1000. default is 1.
     * @param type default is "push", option: "schedule"
     * @return CIDResult, an array of cid
     * @throws APIConnectionException connect exception
     * @throws APIRequestException request exception
     */
    public CIDResult getCidList(int count, String type) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(count >= 1 && count <= 1000, "count should not less than 1 or larger than 1000");
        Preconditions.checkArgument(type == null || type.equals("push") || type.equals("schedule"),
                "type should be \"push\" or \"schedule\"");
        ResponseWrapper responseWrapper;
        if (type != null) {
            responseWrapper = _nativeHttpClient.sendGet(_baseUrl + _pushPath + "/cid?count=" + count + "&type=" + type);
        } else {
            responseWrapper = _nativeHttpClient.sendGet(_baseUrl + _pushPath + "/cid?count=" + count);
        }
        return BaseResult.fromResponse(responseWrapper, CIDResult.class);
    }

}
